#!@pathpython@
# -*- python -*-
# Copyright (c) 2005-2006 Arched Rock Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# - Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the
#   distribution.
# - Neither the name of the Arched Rock Corporation nor the names of
#   its contributors may be used to endorse or promote products derived
#   from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
# ARCHED ROCK OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE
#
# Copyright (c) 2006-2007 Intel Corporation
# All rights reserved.
#
# This file is distributed under the terms in the attached INTEL-LICENSE
# file. If you do not find these files, copies can be found by writing to
# Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA,
# 94704.  Attention:  Intel License Inquiry.

# @author Jonathan Hui <jhui@archedrock.com>
# @author David Gay
# @author Kevin Klues <klueska@cs.stanford.edu>
#
# $Revision: 1.8 $
# $Date: 2009-10-28 04:19:20 $
#

from re import match
from sys import *
from xml.dom import *
from xml.dom.minidom import *
from subprocess import Popen, PIPE

from getopt import *
import string
import commands

def print_usage():
  stderr.write("Usage: tos-storage-stm25p [-t] [-s number of sectors] [--tosdir <path to tos directory>] <platform directory>\n")

#New way of handling arguments........
try:
  opts, args = getopt(argv[1:], "ts:", ["tosdir="])
except GetoptError, err:
  print str(err) # will print something like "option -a not recognized"
  print_usage()
  quit(1)

if len( args ) == 1:
  platformdir = args[0]
  # This gives the whole string when there's no / in platformdir
  platform = platformdir[platformdir.rfind( "/" ) + 1:]
elif len( args ) == 0:
  platformdir = ""
  platform = ""
else:
  print_usage()
  quit(1)

cthreads = False
NUM_SECTORS = 16
SECTOR_SIZE = 65536

tosdir = ""
for o, a in opts:
  if o == "-t":
    cthreads = True
  elif o == "-s":
    NUM_SECTORS = int(a)
  elif o == "--tosdir":
    tosdir = str(a)
  else:
    assert False, "unhandled option"

def error_exit( s ):
    stderr.write( "ERROR: " + s + "\n" )
    exit( 2 )

def expand_path( path ):
  substrs = path.split( "%" )
  path = substrs[0]
  i = 1
  while i < len( substrs ):
    if substrs[i] == "":
      # There was a %%, leading to a blank substring, and the next string
      # should just be appended
      path += "%"
      i = i + 1
      if i < len( substrs ):
        path += substrs[i]
    else:
      # The first character of the string is the one that followed %
      c = substrs[i][0]
      if c == 'p':
        sub = platform
      elif c == 'P':
        sub = platformdir
      elif c == 'T':
        sub = tosdir
      else:
        nfail( "unknown include-path substitution character " + c )
      path += sub
      path += substrs[i][1:]
    i = i + 1
  return path


volumes = {}

volumeNames = []
volumeSizes = []
volumeOffsets = []
volumeTypes = dict()
volumeOptions = dict()
freeSectors = NUM_SECTORS*[ True ]

def volumeparse( file, fname, depth ):
  if depth > 10:
    error_exit( "include nesting too deep - check for cycles" )
  try:
    dom = parse( file )
  except xml.parsers.expat.ExpatError:
    error_exit( fname + " is not a valid input file" )
  except IOError:
    error_exit( "couldn't open file " + fname )

  # extract information
  for volume in dom.documentElement.getElementsByTagName( "volume" ):
      name = volume.getAttribute( "name" )
      size = volume.getAttribute( "size" )
      base = volume.getAttribute( "base" )
      type = string.lower(volume.getAttribute("type"))
      isCircular = string.upper(volume.getAttribute("circular"))
      if isCircular == "":
          isCircular = "FALSE"

      if name == "":
          error_exit( "volume has no name" )
      elif not match( "^[a-zA-Z0-9_]+$", name ):
          error_exit( "volume has invalid name '%s'" % name )
      elif volumes.has_key( name ):
          error_exit( "duplicate volume definition '%s'" % name )
      else:
          volumes[ name ] = "blah"

      if size == "":
          error_exit( "volume '%s' has no size" % name )
      try:
          size = int( size )
      except ValueError:
          error_exit( "volume '%s' has invalid size" % name )
      if base != "":
          try:
              base = int( base )
          except ValueError:
              error_exit( "volume '%s' has invalid base" % name )
      if ( size & ( SECTOR_SIZE - 1 ) ) != 0:
          error_exit( "size of volume '%s' is not a multiple of %d" % \
                      ( name, SECTOR_SIZE ) )
      if base != "" and ( base & ( SECTOR_SIZE - 1 ) ) != 0:
          error_exit( "base of volume '%s' is not a multiple of %d" % \
                      ( name, SECTOR_SIZE ) )

      volumeNames.append( "VOLUME_" + name )
      volumeSizes.append( size / SECTOR_SIZE )
      volumeTypes["VOLUME_" + name] = type
      volumeOptions["VOLUME_" + name] = isCircular

      if base == "":
          volumeOffsets.append( -1 )
      else:
          base = base / SECTOR_SIZE
          size = size / SECTOR_SIZE
          volumeOffsets.append( base )
          for i in range( size ):
              freeSectors[ i + base ] = False

  for include in dom.documentElement.getElementsByTagName( "include" ):
    included = include.firstChild
    if included != None and included.nodeType == included.TEXT_NODE:
      included = expand_path( included.data )
      volumeparse( included, "(file %s)" % included, depth + 1 )
    else:
      error_exit( "invalid include directive " + fname )
  dom.unlink( )

volumeparse( stdin, "(standard input)", 0 )

# allocate with first fit policy
for i in range( len( volumeOffsets ) ):
    size = volumeSizes[ i ]
    if volumeOffsets[ i ] == -1:
        for j in range( NUM_SECTORS ):
            if freeSectors[ j ]:
                size -= 1
                if size == 0:
                    volumeOffsets[ i ] = j - ( volumeSizes[ i ] - 1 )
                    break
            else:
                size = volumeSizes[ i ]
        if volumeOffsets[ i ] == -1:
            raise "Unable to satisfy allocation request."
        else:
            for j in range( volumeSizes[ i ] ):
                freeSectors[ volumeOffsets[ i ] + j ] = False

# output C file

print "#ifndef __STORAGE_VOLUME_H__"
print "#define __STORAGE_VOLUME_H__"
print ""
print "#include \"Stm25p.h\""
print ""

for i in range( len( volumeNames ) ):
    print "#define %s %d" % ( volumeNames[ i ], i )
print ""

print "static const stm25p_volume_info_t STM25P_VMAP[ %d ] = {" % \
      len( volumeNames )
for i in range( len( volumeNames ) ):
    print "    { base : %d, size : %d }," % \
          ( volumeOffsets[ i ], volumeSizes[ i ] )
print "};"

print ""
print "#endif"

# output nc file for threads
if cthreads == True:
  outFile = open(commands.getstatusoutput("pwd")[1] + "/VolumeMapC.nc", "w")
  outFile.write("#include \"StorageVolumes.h\" \n")
  outFile.write("\n")
  outFile.write("configuration VolumeMapC { \n")
  outFile.write("  provides { \n")
  outFile.write("    interface BlockRead[uint8_t volume_id]; \n")
  outFile.write("    interface BlockWrite[uint8_t volume_id]; \n")
  outFile.write("    interface LogRead[uint8_t volumeId]; \n")
  outFile.write("    interface LogWrite[uint8_t volumeId]; \n")
  outFile.write("    interface Mount[uint8_t volumeId]; \n")
  outFile.write("    interface ConfigStorage[uint8_t volumeId]; \n")
  outFile.write("  } \n")
  outFile.write("} \n")
  outFile.write("\n")
  outFile.write("implementation { \n")
  outFile.write("  components VolumeMapP; \n")
  outFile.write("\n")
  outFile.write("  BlockRead = VolumeMapP; \n")
  outFile.write("  BlockWrite = VolumeMapP; \n")
  outFile.write("  LogRead = VolumeMapP; \n")
  outFile.write("  LogWrite = VolumeMapP; \n")
  outFile.write("  Mount = VolumeMapP; \n")
  outFile.write("  ConfigStorage = VolumeMapP; \n")

  for i in range(len(volumeNames)):
    if volumeTypes[volumeNames[i]] == "block":
      outFile.write("\n")
      outFile.write("  components new BlockStorageC(" + volumeNames[i] + ") as BlockStorageC_" + volumeNames[i] + "; \n")
      outFile.write("  VolumeMapP.SubBlockRead[" + volumeNames[i] + "] -> BlockStorageC_" + volumeNames[i] + "; \n")
      outFile.write("  VolumeMapP.SubBlockWrite[" + volumeNames[i] + "] -> BlockStorageC_" + volumeNames[i] + "; \n")
      outFile.write("\n")

    elif volumeTypes[volumeNames[i]] == "log":
      outFile.write("\n")
      outFile.write("  components new LogStorageC(" + volumeNames[i] + ", " + volumeOptions[volumeNames[i]] + ") as LogStorageC_" + volumeNames[i] + "; \n")
      outFile.write("  VolumeMapP.SubLogRead[" + volumeNames[i] + "] -> LogStorageC_" + volumeNames[i] + "; \n")
      outFile.write("  VolumeMapP.SubLogWrite[" + volumeNames[i] + "] -> LogStorageC_" + volumeNames[i] + "; \n")
      outFile.write("\n")

    elif volumeTypes[volumeNames[i]] == "config":
      outFile.write("  components new ConfigStorageC(" + volumeNames[i] + ") as ConfigStorageC_" + volumeNames[i] + "; \n")
      outFile.write("  Mount[" + volumeNames[i] + "] = ConfigStorageC_" + volumeNames[i] + "; \n")
      outFile.write("  ConfigStorage[" + volumeNames[i] + "] = ConfigStorageC_" + volumeNames[i] + "; \n")
  outFile.write("} \n")
